﻿using LightCAD.Core;
using LightCAD.Runtime;
using LightCAD.Three;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Reflection.Emit;
using System.Xml.Linq;
using static System.Formats.Asn1.AsnWriter;
using Color = LightCAD.MathLib.Color;

namespace LightCAD.Model
{

    public class Model3DEditRuntime : Doc3dEditRuntime
    {
        public enum ControlsTypeEnum
        {
            Su,
            Revit,
            LightCAD
        }
        private SuControls suControls;
        private RvtControls rvtControls;
        private OrbitControls orbControls;
        private Group ModelGroup;
        private Group SectionGroup;
        private Group nobindDrawingsGroup;
        private Group buildingsGroup;
        private ConcurrentDictionary<LcBuilding, Group> buildingGroup = new ConcurrentDictionary<LcBuilding, Group>();
        private ClipBox clipBox;

        public IModel3DEditControl Control { get; set; }
        public ILightCADControls CameraControl;

        public Model3DEditRuntime(DocumentRuntime docRt, IModel3DEditControl control, ICommandControl commandControl) : base(docRt, control, commandControl)
        {
            this.Name = "模型";
            this.EditorType = EditorType.Model3d;
            this.Control = control;
            this.DocRt.Document.ModelSpace.ObjectChangedAfter += ModelSpace_ObjectChangedAfter;
            this.DocRt.Document.ModelSpace.PropertyChangedAfter += ModelSpace_PropertyChangedAfter;
            this.DocRt.Action.SelectsChanged += DocumentAction_SelectsChanged;
            this.Control.OnLevelFilterChanged += Control_OnLevelFilterChanged;
            this.CommandCenter = new CommandCenter(this, commandControl);
            this.SelectMats.ForEach(s => s.clippingPlanes = Clipplanes);

        }

        public void ChangeControls(ControlsTypeEnum conType)
        {
            switch (conType)
            {
                case ControlsTypeEnum.Su:
                    this.orbControls.Enable(false);
                    this.rvtControls.Enable(false);
                    this.suControls.Clone(this.CameraControl);
                    this.CameraControl = suControls;
                    this.CameraControl.Enable(true);
                    break;
                case ControlsTypeEnum.Revit:
                    this.orbControls.Enable(false);
                    this.suControls.Enable(false);
                    this.rvtControls.Clone(this.CameraControl);
                    this.CameraControl = rvtControls;
                    this.CameraControl.Enable(true);
                    break;
                case ControlsTypeEnum.LightCAD:
                    this.suControls.Enable(false);
                    this.rvtControls.Enable(false);
                    this.orbControls.Clone(this.CameraControl);
                    this.CameraControl = orbControls;
                    this.CameraControl.Enable(true);
                    break;
                default:
                    break;
            }
        }
        public string ShowSections()
        {
            return this.Control?.InvokeMethod("ShowSections")?.ToString();
        }

        private void Control_OnLevelFilterChanged(List<LcLevel> levels)
        {
            foreach (var item in this.cptNodes)
            {
                var nodes = item.Value;
                for (int i = 0; i < nodes.Count; i++)
                {
                    var node = nodes[i];
                    var visible = levels.Contains(GetLevel(node));
                    nodeObj3ds[node].ForEach(o => o.visible = visible);
                }
                
            }
            this.Control.RefreshControl();
        }

        private ListEx<Material> SelectMats = new ListEx<Material> { new MeshBasicMaterial { color = new Color(0x0000FF), transparent = true, opacity = 0.5 } };
        private ConcurrentDictionary<int, ListEx<Material>> OriginMaterials = new ConcurrentDictionary<int, ListEx<Material>>();

      
        //private Mesh testBox;
        protected override void OnInit()
        {
            base.OnInit();
            this.camera.position.Copy(syncCameraOffset);
            this.camera.lookAt(new Vector3());
            this.ModelGroup = new Group();
            this.ModelGroup.name = "模型组";
            scene.add(this.ModelGroup);

            this.SectionGroup = new Group();
            this.SectionGroup.name = "剖切组";
            this.SectionGroup.visible = false;
            scene.add(this.SectionGroup);

            this.clipBox = new ClipBox(this.SectionGroup, UnSelectChannel);

            this.nobindDrawingsGroup = new Group();
            this.nobindDrawingsGroup.name = "未使用的图框元素组";
            this.ModelGroup.add(this.nobindDrawingsGroup);

            this.buildingsGroup = new Group();
            this.buildingsGroup.name = "单体元素组";
            this.ModelGroup.add(this.buildingsGroup);

            camera.layers.enable(ClipPlane.ControlPointLayerChanel);
            this.suControls = new SuControls(camera, this.Control)
            {
                screenSpacePanning = true,
                enabled = false
            };

            this.suControls.AddEventListener("change", (e) =>
            {
                this.Control.RefreshControl();
            });

            this.rvtControls = new RvtControls(camera, this.Control)
            {
                screenSpacePanning = true,
                enabled = false
            };

            this.rvtControls.AddEventListener("change", (e) =>
            {
                this.Control.RefreshControl();
            });

            this.orbControls = new OrbitControls(camera, this.Control)
            {
                screenSpacePanning = true
            };

            this.orbControls.AddEventListener("change", (e) =>
            {
                this.Control.RefreshControl();
            });

            this.CameraControl = this.orbControls;

            this.modelSpace = GetModelSpace(this.DocRt.Document.ModelSpace);
            this.UpdateTask = Task.Run(() => UpdateLoop());
        }
        private Task UpdateTask;
        protected override void OnDestory()
        {
            base.OnDestory();
            this.CameraControl.Dispose();
        }

        protected override void OnSizeChanged(Bounds bounds)
        {
            base.OnSizeChanged(bounds);
            this.CameraControl.Update();
        }
        protected override void OnMouseDown(MouseEventRuntime e)
        {
            downTime = DateTime.Now;
            RayCasterClipBoxControlPoint();
        }
        protected override void OnMouseMove(MouseEventRuntime e)
        {
            base.OnMouseMove(e);
            if (clipMove)
            {
                clipPlane.Move(camera, mouseLoc);
                clipBox.ClipModel(this.ModelGroup, true);
                this.Control.RefreshControl();
            }
        }
        protected override void OnMouseUp(MouseEventRuntime e)
        {
            if (clipMove)
            {
                clipMove = false;
                clipPlane.UnDrag();
                this.CameraControl.Enable(true);
            }
            else
            {
                if (downTime != null && (DateTime.Now - downTime).Value.TotalMilliseconds <= 200)
                {
                    if (EnableSelect)
                    {
                        var intersects = RayCaster();
                        bool needRefresh = false;
                        if (this.SectionGroup.visible)
                        {
                            intersects.RemoveAll(inter => !clipBox.Box.ContainsPoint(inter.point));
                        }
                        var interSect = intersects.FirstOrDefault(o => o.target is Mesh);
                        if (interSect != null)
                        {
                            var mesh = interSect.target as Mesh;
                            var node = mesh.ext["Node"] as ModelNode;
                            var selEle = node.Component as LcElement;
                            if (selEle != null)
                            {
                                SelectEle(new List<LcElement> { selEle });
                            }
                            for (int i = 0; i < this.nodeObj3ds[node].Count; i++)
                            {
                                var eObj3d = this.nodeObj3ds[node][i];
                                if (eObj3d is Mesh cMesh)
                                    SelectMesh(cMesh);
                            } 
                        }
                        else
                        {
                            var ele3Ds = this.DocRt.Action.SelectedElements.FindAll(e => e is IElement3d);
                            UnSelectEle(ele3Ds);
                            var meshes = SelectedMeshs.Values.ToList();
                            for (int i = 0; i < meshes.Count; i++)
                            {
                                var mesh = meshes[i];
                                UnSelectMesh(mesh);
                                needRefresh = true;
                            }
                        }
                        if (needRefresh)
                            this.Control.RefreshControl();
                    }
                }
            }
            downTime = null;

        }

        #region Events


        private bool clipMove = false;
        private ClipPlane clipPlane = null;
        private DateTime? downTime = null;
        Raycaster EleRaycaster = new Raycaster();
        private void RayCasterClipBoxControlPoint()
        {
            var raycaster = new Raycaster().setFromCamera(mouseLoc, camera);
            raycaster.layers.set(ClipPlane.ControlPointLayerChanel);
            var intersects = raycaster.intersectObject(this.scene, true);
            if (intersects.Count > 0)
            {
                var mesh = intersects[0].target as Mesh;
                if (mesh.ext.ContainsKey("ClipPlane"))
                {
                    clipMove = true;
                    this.CameraControl.Enable(false);
                    clipPlane = mesh.ext["ClipPlane"] as ClipPlane;
                    clipPlane.Drag();
                }
                this.Control.RefreshControl();
            }
        }

        #region Hover

        private bool isPointerHovering;
        private System.Timers.Timer hoverTimer;
        private void HoverTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            hoverTimer.Stop();
            this.isPointerHovering = true;
            HoverEventStart();
        }
        private void HoverEventStart()
        {

        }
        private void HoverEventEnd()
        {

        }

        #endregion

        public bool EnableSelect { get; set; } = true;
        private ConcurrentDictionary<int, Mesh> SelectedMeshs = new ConcurrentDictionary<int, Mesh>();
        private void DocumentAction_SelectsChanged(object? sender, System.EventArgs e)
        {
            var sv = SelectedMeshs.Values.ToList();
            sv.ForEach(mesh => { UnSelectMesh(mesh); });
            var selEles = this.DocRt.Action.SelectedElements.FindAll(e => e is IComponentInstance);
            selEles.ForEach(se =>
            {
                var cptIns = se as IComponentInstance;
                var nodes = this.cptNodes.ContainsKey(cptIns)? this.cptNodes[cptIns]:new List<ModelNode>();
                for (int i = 0; i < nodes.Count; i++)
                {
                    var node = nodes[i];    
                    this.nodeObj3ds[node].ForEach(o => { if (o is Mesh m) SelectMesh(m); });
                }
                
            });
            this.Control.RefreshControl();
        }
        private void SelectMesh(Mesh mesh)
        {
            OriginMaterials.GetOrAdd(mesh.id, mesh.materials);
            mesh.materials = SelectMats;
            this.SelectedMeshs.GetOrAdd(mesh.id, mesh);
        }
        private void UnSelectMesh(Mesh mesh)
        {
            if (mesh.materials == SelectMats)
                mesh.materials = OriginMaterials[mesh.id];
            this.SelectedMeshs.Remove(mesh.id, out var nmesh);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="selEles"></param>
        private void SelectEle(List<LcElement> selEles)
        {
            try
            {
                this.DocRt.Action.SelectElements(this, selEles);
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message + ex.StackTrace);
            }
            finally
            {
            }
        }
        private void UnSelectEle(List<LcElement> selEles)
        {
            this.DocRt.Action.ClearSelects(this, selEles.FindAll(e => e.IsSelected));
        }

        #endregion
        public override Group GetExtGroup(string grpName)
        {
            var basicGroup = new string[] { "模型组", "剖切组", "无图元素组", "未使用的图框元素组", "单体元素组" };
            if (basicGroup.Contains(grpName))
                return null;
            return base.GetExtGroup(grpName);
        }


      
        private ListEx<Plane> Clipplanes = new ListEx<Plane>();
        private readonly Vector3 syncCameraOffset = new Vector3(-10000, -10000, 50000);
        //public virtual void SyncViewPort2D(ViewportRuntime  viewportRt) 
        //{
        //    if (this.camera is OrthographicCamera)
        //    {
        //        var vc = viewportRt.DcsToWcs.MultiplyPoint(new Vector2d(0, 0));
        //        var scale = viewportRt.Viewport.Scale;
        //        var orCamera= (OrthographicCamera)this.camera;
        //        orCamera.zoom = scale/2*((double)canvas.Width/viewportRt.Viewport.Width);
        //        orCamera.position.AddVectors(new Vector3(vc.X,vc.Y,0), syncCameraOffset);
        //        var target = new Vector3(vc.X, vc.Y, 0);
        //        //orCamera.up.set(0, 1, 0);
        //        orCamera.lookAt(target);
        //        controls.target.Copy(target);
        //    }
        //    this.Control.RefreshControl();
        //}
        private bool clipBoxEnable = false;
        public void EnbaleClipBox()
        {
            clipBoxEnable = !clipBoxEnable;
            if (clipBoxEnable)
            {
                this.SectionGroup.visible = true;
                this.clipBox.Set(GeoUtil.ComputeWorldBBox(this.ModelGroup), this.Clipplanes);
                this.renderer.localClippingEnabled = true;
            }
            else
            {
                this.SectionGroup.visible = false;
                this.renderer.localClippingEnabled = false;
            }
            this.Control.RefreshControl();
        }
        public override void EnableCameraControl(bool enable)
        {
            this.CameraControl.EnableRotate(enable);
            this.CameraControl.Enable(enable);
            this.CameraControl.ClearState();
        }
        public override ListEx<Raycaster.Intersection> RayCaster(Raycaster eleRaycaster = null, Action<Camera> cameraAction = null)
        {
            if (eleRaycaster == null)
                eleRaycaster = EleRaycaster;
            return base.RayCaster(eleRaycaster, cameraAction);
        }

        private object lckObj = new object();
        private void ModelSpace_PropertyChangedAfter(object? sender, PropertyChangedEventArgs e)
        {
            if (e.Object is IComponentInstance cptIns)
            {
                Task.Run(() => { 
                lock (lckObj)
                {
                    var ele = cptIns as LcElement;
                    if (ele.Parent == this.DocRt.Document.ModelSpace)
                    {
                        LcDrawing matchDrawing = null;
                        LcDrawing oldDrawing = null;
                        bool inOldDrawing = false;
                        foreach (var item in drawCpts)
                        {
                            var drawing = item.Key;
                            var cpts = item.Value;
                            drawModels[drawing].ForEach(n => n.Transform.Position.Set(-drawing.buildingBasePoint.Point.X, -drawing.buildingBasePoint.Point.Y, 0));
                            bool isOld = cpts.Contains(cptIns);
                            if (matchDrawing == null)
                            {
                                bool isInDrawing = drawing.BoundingBox.IntersectsBox(ele.BoundingBox);
                                if (isInDrawing)
                                {
                                    matchDrawing = drawing;
                                    //框内移动或者变化
                                    if (isOld)
                                    {
                                        oldDrawing = drawing;
                                        this.cptNodes[cptIns].ForEach(n =>
                                        {
                                            if (n.DirtyType == DirtyType.None)
                                                n.DirtyType = DirtyType.Change;
                                            else
                                                n.DirtyType = n.DirtyType;
                                        });
                                        inOldDrawing = true;
                                        continue;
                                    }

                                }
                            }
                            if (isOld)
                            {
                                oldDrawing = drawing;
                                this.drawCpts[oldDrawing].Remove(cptIns);
                                this.cptNodes[cptIns].ForEach(n => n.DirtyType = DirtyType.Delete);
                            }
                        }
                        if (!inOldDrawing && matchDrawing != null)
                        {
                            this.drawCpts[matchDrawing].Add(cptIns);
                            drawModels[matchDrawing].ForEach(g =>
                            {
                                var basePoint = matchDrawing.buildingBasePoint.Point;
                                g.Transform.Position.Set(-basePoint.X, -basePoint.Y, 0);
                                g.AddNodes(new ModelNode { Component = cptIns });
                            });
                        }
                        else
                        {
                            if (oldDrawing == null)
                            { this.cptNodes[cptIns].ForEach(n => {
                                if (n.DirtyType ==DirtyType.None)
                                    n.DirtyType = DirtyType.Change;
                            }); }
                        }


                    }
                }
                });
            }
        }
        private void ModelSpace_ObjectChangedAfter(object? sender, ObjectChangedEventArgs e)
        {
            var target = e.Target;
            Task.Run(() => {
                lock (lckObj)
            {
                if (target is IComponentInstance cptIns)
                {
                    if (e.Type == ObjectChangeType.Remove)
                    {
                        if (this.cptNodes.ContainsKey(cptIns))
                            this.cptNodes[cptIns].ForEach(n => n.DirtyType = DirtyType.Delete);
                    }
                    else if (e.Type == ObjectChangeType.Insert && sender == target)
                    {
                        var ele = cptIns as LcElement;
                        bool matchDrawing = false;
                        foreach (var item in drawCpts)
                        {
                            var drawing = item.Key;
                            if (drawing.BoundingBox.IntersectsBox(ele.BoundingBox))
                            {
                                item.Value.Add(cptIns);
                                drawModels[drawing].ForEach(g =>
                                {
                                    var basePoint = drawing.buildingBasePoint.Point;
                                    g.Transform.Position.Set(-basePoint.X, -basePoint.Y, 0);
                                    g.AddNodes(new ModelNode { Component = cptIns });
                                }
                                );
                                matchDrawing = true;
                                break;
                            }
                        }
                        if (!matchDrawing)
                            this.modelSpace.Areas[0].Sites[0].Groups[0].AddNodes(new ModelNode { Component = cptIns });
                    }
                }
                else if (target is LcDrawing drawing)
                {
                    if (e.Type == ObjectChangeType.Insert && sender == target)
                    {
                        foreach (var building in this.DocRt.Document.Buildings)
                        {
                            if (building.Parent == null)
                                this.modelSpace.Areas[0].AddSites(building);
                            foreach (var lvl in building.Levels)
                            {
                                if (lvl.Drawings.Contains(drawing))
                                {

                                    lvl.AddGroups(GetDrawingGroup(drawing));
                                }
                            }
                        }
                    }
                    else if (e.Type == ObjectChangeType.Remove)
                    {
                        var models = drawModels.GetOrAdd(drawing, new List<ModelGroup>());
                        for (int i = 0; i < models.Count; i++)
                        {
                            var model = models[i];
                            model.DirtyType = DirtyType.Delete;
                        }
                        drawCpts.Remove(drawing, out var cpts);
                    }
                }
                }
            });
        }


        private ConcurrentDictionary<LcDrawing,List< IComponentInstance>> drawCpts=new ConcurrentDictionary<LcDrawing, List<IComponentInstance>>();
        private ConcurrentDictionary<LcDrawing,List< ModelGroup>> drawModels=new ConcurrentDictionary<LcDrawing, List<ModelGroup>>();
        ModelSpace GetModelSpace(ElementSpace eleSpace)
        {
            var drawings = eleSpace.Elements.Where(e =>
            {
                if (e is LcDrawing dw)
                    return dw.buildingBasePoint != null;
                return false;
            }).Select(e => e as LcDrawing).ToListEx();
            var bindedDrawings = drawings.FindAll(d =>
            eleSpace.Document.Buildings.FirstOrDefault(b =>
            b.Levels.FirstOrDefault(l => l.Drawings.Contains(d)) != null) != null);
            var noBindDrawings = drawings.FindAll(d => !bindedDrawings.Contains(d));
            //通过图纸进行元素分组

            var modelSpace = new ModelSpace();
            var modelArea = new ModelArea();
            var worldGrp = new ModelGroup() { Name = "世界" };
            var worldSite = new ModelSite() { Name = "世界" }.AddGroups(worldGrp);
            drawCpts.Clear();
            foreach (var ele in eleSpace.Elements)
            {
                if (ele is LcDrawing) continue;
                if (drawings.Length > 0)
                {
                    var isAdded = false;
                    foreach (LcDrawing drawing in drawings)
                    {
                        var cpts = drawCpts.GetOrAdd(drawing, (c) => new List<IComponentInstance>());
                        if (ele.BoundingBox.IntersectsBox(drawing.BoundingBox))
                        {
                            if (ele is IComponentInstance cpt)
                                cpts.Add(cpt);
                            else if(ele is LcBasePoint bp)
                                drawing.buildingBasePoint= bp;
                            isAdded = true;
                        }
                    }
                    if (!isAdded)
                    {
                        if (ele is IComponentInstance cpt)
                        {
                            worldGrp.AddNodes(new ModelNode() { Component = cpt });
                        }
                    }
                }
                else
                {
                    if (ele is IComponentInstance cpt)
                    {
                        worldGrp.AddNodes(new ModelNode() { Component = cpt });
                    }
                }
            }

            modelSpace.AddAreas(modelArea);
            modelArea.AddSites(worldSite);
            foreach (var building in eleSpace.Document.Buildings)
            {
                foreach (var lvl in building.Levels)
                {
                    lvl.Transform.Position.Z = lvl.Elevation;
                    foreach (var drawing in lvl.Drawings)
                    {
                        lvl.AddGroups(GetDrawingGroup(drawing));
                    }
                }
                modelArea.AddSites(building);
            }
            foreach (var drawing in noBindDrawings)
            {
                worldGrp.AddGroups(GetDrawingGroup(drawing));
            }
            return modelSpace;
        }
        private ModelGroup GetDrawingGroup(LcDrawing drawing)
        {
            var cpts  = drawCpts.GetOrAdd(drawing, (c) => new List<IComponentInstance>()); 
            var grps = new ModelGroup();
            grps.Transform.Position.X -= drawing?.buildingBasePoint?.Point?.X??0;
            grps.Transform.Position.Y -= drawing?.buildingBasePoint?.Point?.Y ?? 0;
            for (int i = 0; i < cpts.Count; i++)
            {
                var cpt = cpts[i];
                grps.AddNodes(new ModelNode { Component = cpt });
            }
            var drawModelGrps = drawModels.GetOrAdd(drawing, new List<ModelGroup>());
            drawModelGrps.Add(grps);
            return grps;
        }
        //private List<string>usedNames=new List<string>();
        void RenderModelSpace(ModelSpace modelSpace)
        {
            if (modelSpace.DirtyType!=DirtyType.None)
            {
                for (int i = 0; i < modelSpace.Areas.Length; i++)
                {
                    var area = modelSpace.Areas[i];
                    if (area.DirtyType!= DirtyType.None) 
                    {
                        area.Transform.Update();
                        area.UpdateWorldMatrix();
                        RenderArea(area);
                        area.DirtyType = DirtyType.None;
                    }
                }
                modelSpace.DirtyType = DirtyType.None;
                this.Control.RefreshControl();
            }
        }
        void RenderArea(ModelArea area)  
        {
            for (int i = 0; i < area.Sites.Length; i++)
            {
                var site = area.Sites[i];
                if (site.DirtyType == DirtyType.None)
                {
                    continue;
                }
                if (site.DirtyType == DirtyType.Delete) 
                {
                    area.Sites.RemoveAt(i);
                    i--;
                }
                else
                {
                    site.Transform.Update();
                    site.UpdateWorldMatrix();
                    for (int j = 0; j < site.Groups.Length; j++)
                    {
                        var grp = site.Groups[j];
                        if (grp.DirtyType == DirtyType.None)
                            continue;
                        if (grp.DirtyType == DirtyType.Delete)
                        {
                            site.Groups.RemoveAt(j);
                            j--;
                        }
                        else
                        {
                            grp.Transform.Update();
                            grp.UpdateWorldMatrix();
                            RenderGroup(grp);
                        }
                        grp.DirtyType = DirtyType.None;
                    }
                }
                site.DirtyType = DirtyType.None;
            } 
        }
        void RenderGroup(ModelGroup grp) 
        {
            for (int i = 0; i<grp.Groups.Length; i++) 
            {
                var subGrp = grp.Groups[i];
                if (subGrp.DirtyType == DirtyType.None)
                    continue;
                if (subGrp.DirtyType == DirtyType.Delete)
                {
                    grp.Groups.RemoveAt(i);
                    i--;
                }
                else
                {
                    subGrp.Transform.Update();
                    subGrp.UpdateWorldMatrix();
                    RenderGroup(subGrp);
                }
            }
            for (int i = 0; i < grp.Nodes.Length; i++) 
            {
                var node = grp.Nodes[i];
                if (node.DirtyType == DirtyType.None)
                    continue;
                if (node.DirtyType == DirtyType.Delete)
                {
                    this.nodeObj3ds[node].ForEach(o => o.removeFromParent());
                    grp.Nodes.RemoveAt(i);
                    i--;
                }
                else
                {
                    node.Transform.Update();
                    node.UpdateWorldMatrix();
                    RenderNode(node);
                    node.DirtyType = DirtyType.None;
                }
            }    
        }

        private ConcurrentDictionary<IComponentInstance, List<ModelNode>> cptNodes = new ConcurrentDictionary<IComponentInstance, List<ModelNode>>();
        private ConcurrentDictionary<ModelNode, List<Object3D>> nodeObj3ds = new ConcurrentDictionary<ModelNode, List<Object3D>>();
        void RenderNode(ModelNode node) 
        {
            var cptIns = node.Component;
            var cptAction = cptIns.Rt3DAction as IComponentAction;
            if (!nodeObj3ds.ContainsKey(node))
            {
                var nodes = cptNodes.GetOrAdd(cptIns, new List<ModelNode>());
                nodes.Add(node);
            }
            
            cptAction.SetDocEditor(this);
            var objs= cptAction.Render(cptIns);
            var building = GetBuilding(node);
            nodeObj3ds.AddOrUpdate(node, (n) => 
            {
                if (building == null)
                {
                    this.nobindDrawingsGroup.add(objs.ToArray());
                }
                else
                {
                    this.buildingGroup.GetOrAdd(building, (b) =>
                    {
                        var grp = new Group() { name = b.Name };
                        this.buildingsGroup.add(grp);
                        return grp;
                    });
                    this.buildingGroup[building].add(objs.ToArray());
                }
                return objs; 
            },(n,os)=> 
            {
                for (int i = 0; i < os.Count; i++)
                {
                    os[i].removeFromParent();
                }
                if (building == null)
                {
                    this.nobindDrawingsGroup.add(objs.ToArray());
                }
                else
                {

                    this.buildingGroup.GetOrAdd(building, (b) =>
                    {
                        var grp = new Group() { name = b.Name };
                        this.buildingsGroup.add(grp);
                        return grp;
                    });
                    this.buildingGroup[building].add(objs.ToArray());
                }
                
                return objs;
                });
            var isMirror = node.WorldMatrix.Determinant() < 0 ^(cptIns.Transform3d?.IsMirror??false);
            for (int i = 0; i < objs.Count; i++)
            {
                var obj = objs[i];
                //OBJ要被Clipplanes剖切
                if (obj is IMaterialObject matObj)
                {
                    var mats = matObj.getMaterials();
                    for (int j = 0; j < mats.Length; j++)
                    {
                        mats[j].clippingPlanes = Clipplanes;
                    }
                }
                if (obj is Mesh mesh && isMirror)
                {
                    var oldIndexs = mesh.geometry.index.intArray;
                    for (var k=0;k < oldIndexs.Length;k+=3)
                    {
                        var temp = oldIndexs[k];
                        oldIndexs[k] = oldIndexs[k + 2];
                        oldIndexs[k + 2] = temp;
                    }
                }
                obj.applyMatrix4(node.WorldMatrix);
                obj.name = cptIns.Definition.Name;
                obj.ext["Node"] = node;
            }
        }

        LcLevel GetLevel(ModelObject obj)
        {
            while (obj != null) 
            {
                if (obj is LcLevel)
                    return obj as LcLevel;
                obj = obj.Parent as ModelObject;
            }
            return null;
        }
        LcBuilding GetBuilding(ModelObject obj)
        {
            while (obj != null)
            {
                if (obj is LcBuilding)
                    return obj as LcBuilding;
                obj = obj.Parent as ModelObject;
            }
            return null;
        }

        #region Update
        private ModelSpace modelSpace;
        void UpdateLoop() 
        {
            while (true) 
            {
                if (modelSpace != null)
                    try
                    {
                        lock (lckObj) { 
                        RenderModelSpace(modelSpace);
                        }
                    }
                    catch (Exception ex)
                    {

                    }
                   
            }
        }
        #endregion

    }

}
